/*
  CoveWhsFs
  Copyright (C) 2011 Alex Piskarev  
  http://code.google.com/p/covewhsfs/
  
  Original work:
  Dokan
  Copyright (C) 2010 Hiroki Asakawa info@dokan-dev.net
  http://dokan-dev.net/en

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "sys.h"

VOID DDbgPrintFsctl(
    __in LONG   FsControlCode
    )
{
    switch(FsControlCode) {

	case FSCTL_REQUEST_OPLOCK_LEVEL_1:
		DDbgPrint("    FSCTL_REQUEST_OPLOCK_LEVEL_1\n");
		break;

	case FSCTL_REQUEST_OPLOCK_LEVEL_2:
		DDbgPrint("    FSCTL_REQUEST_OPLOCK_LEVEL_2\n");
		break;

	case FSCTL_REQUEST_BATCH_OPLOCK:
		DDbgPrint("    FSCTL_REQUEST_BATCH_OPLOCK\n");
		break;

    case FSCTL_REQUEST_OPLOCK:
		DDbgPrint("    FSCTL_REQUEST_OPLOCK\n");
		break;
        
	case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
		DDbgPrint("    FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n");
		break;

	case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
		DDbgPrint("    FSCTL_OPBATCH_ACK_CLOSE_PENDING\n");
		break;

	case FSCTL_OPLOCK_BREAK_NOTIFY:
		DDbgPrint("    FSCTL_OPLOCK_BREAK_NOTIFY\n");
		break;

	case FSCTL_LOCK_VOLUME:
		DDbgPrint("    FSCTL_LOCK_VOLUME\n");
		break;

	case FSCTL_UNLOCK_VOLUME:
		DDbgPrint("    FSCTL_UNLOCK_VOLUME\n");
		break;

	case FSCTL_DISMOUNT_VOLUME:
		DDbgPrint("    FSCTL_DISMOUNT_VOLUME\n");
		break;

	case FSCTL_IS_VOLUME_MOUNTED:
		DDbgPrint("    FSCTL_IS_VOLUME_MOUNTED\n");
		break;

	case FSCTL_IS_PATHNAME_VALID:
		DDbgPrint("    FSCTL_IS_PATHNAME_VALID\n");
		break;

	case FSCTL_MARK_VOLUME_DIRTY:
		DDbgPrint("    FSCTL_MARK_VOLUME_DIRTY\n");
		break;

	case FSCTL_QUERY_RETRIEVAL_POINTERS:
		DDbgPrint("    FSCTL_QUERY_RETRIEVAL_POINTERS\n");
		break;

	case FSCTL_GET_COMPRESSION:
		DDbgPrint("    FSCTL_GET_COMPRESSION\n");
		break;

	case FSCTL_SET_COMPRESSION:
		DDbgPrint("    FSCTL_SET_COMPRESSION\n");
		break;

	case FSCTL_MARK_AS_SYSTEM_HIVE:
		DDbgPrint("    FSCTL_MARK_AS_SYSTEM_HIVE\n");
		break;

	case FSCTL_OPLOCK_BREAK_ACK_NO_2:
		DDbgPrint("    FSCTL_OPLOCK_BREAK_ACK_NO_2\n");
		break;

	case FSCTL_INVALIDATE_VOLUMES:
		DDbgPrint("    FSCTL_INVALIDATE_VOLUMES\n");
		break;

	case FSCTL_QUERY_FAT_BPB:
		DDbgPrint("    FSCTL_QUERY_FAT_BPB\n");
		break;

	case FSCTL_REQUEST_FILTER_OPLOCK:
		DDbgPrint("    FSCTL_REQUEST_FILTER_OPLOCK\n");
		break;

	case FSCTL_FILESYSTEM_GET_STATISTICS:
		DDbgPrint("    FSCTL_FILESYSTEM_GET_STATISTICS\n");
		break;

	case FSCTL_GET_NTFS_VOLUME_DATA:
		DDbgPrint("    FSCTL_GET_NTFS_VOLUME_DATA\n");
		break;

	case FSCTL_GET_NTFS_FILE_RECORD:
		DDbgPrint("    FSCTL_GET_NTFS_FILE_RECORD\n");
		break;

	case FSCTL_GET_VOLUME_BITMAP:
		DDbgPrint("    FSCTL_GET_VOLUME_BITMAP\n");
		break;

	case FSCTL_GET_RETRIEVAL_POINTERS:
		DDbgPrint("    FSCTL_GET_RETRIEVAL_POINTERS\n");
		break;

	case FSCTL_MOVE_FILE:
		DDbgPrint("    FSCTL_MOVE_FILE\n");
		break;

	case FSCTL_IS_VOLUME_DIRTY:
		DDbgPrint("    FSCTL_IS_VOLUME_DIRTY\n");
		break;

	case FSCTL_ALLOW_EXTENDED_DASD_IO:
		DDbgPrint("    FSCTL_ALLOW_EXTENDED_DASD_IO\n");
		break;

	case FSCTL_FIND_FILES_BY_SID:
		DDbgPrint("    FSCTL_FIND_FILES_BY_SID\n");
		break;

	case FSCTL_SET_OBJECT_ID:
		DDbgPrint("    FSCTL_SET_OBJECT_ID\n");
		break;

	case FSCTL_GET_OBJECT_ID:
		DDbgPrint("    FSCTL_GET_OBJECT_ID\n");
		break;

	case FSCTL_DELETE_OBJECT_ID:
		DDbgPrint("    FSCTL_DELETE_OBJECT_ID\n");
		break;

	case FSCTL_SET_REPARSE_POINT:
		DDbgPrint("    FSCTL_SET_REPARSE_POINT\n");
		break;

	case FSCTL_GET_REPARSE_POINT:
		DDbgPrint("    FSCTL_GET_REPARSE_POINT\n");
		break;

	case FSCTL_DELETE_REPARSE_POINT:
		DDbgPrint("    FSCTL_DELETE_REPARSE_POINT\n");
		break;

	case FSCTL_ENUM_USN_DATA:
		DDbgPrint("    FSCTL_ENUM_USN_DATA\n");
		break;

	case FSCTL_SECURITY_ID_CHECK:
		DDbgPrint("    FSCTL_SECURITY_ID_CHECK\n");
		break;

	case FSCTL_READ_USN_JOURNAL:
		DDbgPrint("    FSCTL_READ_USN_JOURNAL\n");
		break;

	case FSCTL_SET_OBJECT_ID_EXTENDED:
		DDbgPrint("    FSCTL_SET_OBJECT_ID_EXTENDED\n");
		break;

	case FSCTL_CREATE_OR_GET_OBJECT_ID:
		DDbgPrint("    FSCTL_CREATE_OR_GET_OBJECT_ID\n");
		break;

	case FSCTL_SET_SPARSE:
		DDbgPrint("    FSCTL_SET_SPARSE\n");
		break;

	case FSCTL_SET_ZERO_DATA:
		DDbgPrint("    FSCTL_SET_ZERO_DATA\n");
		break;

	case FSCTL_QUERY_ALLOCATED_RANGES:
		DDbgPrint("    FSCTL_QUERY_ALLOCATED_RANGES\n");
		break;

	case FSCTL_SET_ENCRYPTION:
		DDbgPrint("    FSCTL_SET_ENCRYPTION\n");
		break;

	case FSCTL_ENCRYPTION_FSCTL_IO:
		DDbgPrint("    FSCTL_ENCRYPTION_FSCTL_IO\n");
		break;

	case FSCTL_WRITE_RAW_ENCRYPTED:
		DDbgPrint("    FSCTL_WRITE_RAW_ENCRYPTED\n");
		break;

	case FSCTL_READ_RAW_ENCRYPTED:
		DDbgPrint("    FSCTL_READ_RAW_ENCRYPTED\n");
		break;

	case FSCTL_CREATE_USN_JOURNAL:
        DDbgPrint("    FSCTL_CREATE_USN_JOURNAL\n");
		break;

	case FSCTL_READ_FILE_USN_DATA:
		DDbgPrint("    FSCTL_READ_FILE_USN_DATA\n");
		break;

	case FSCTL_WRITE_USN_CLOSE_RECORD:
		DDbgPrint("    FSCTL_WRITE_USN_CLOSE_RECORD\n");
		break;

	case FSCTL_EXTEND_VOLUME:
		DDbgPrint("    FSCTL_EXTEND_VOLUME\n");
		break;

	case FSCTL_QUERY_USN_JOURNAL:
		DDbgPrint("    FSCTL_QUERY_USN_JOURNAL\n");
		break;

	case FSCTL_DELETE_USN_JOURNAL:
		DDbgPrint("    FSCTL_DELETE_USN_JOURNAL\n");
		break;

	case FSCTL_MARK_HANDLE:
		DDbgPrint("    FSCTL_MARK_HANDLE\n");
		break;

	case FSCTL_SIS_COPYFILE:
		DDbgPrint("    FSCTL_SIS_COPYFILE\n");
		break;

	case FSCTL_SIS_LINK_FILES:
		DDbgPrint("    FSCTL_SIS_LINK_FILES\n");
		break;

	case FSCTL_RECALL_FILE:
		DDbgPrint("    FSCTL_RECALL_FILE\n");
		break;
        
    case CTL_CODE(FILE_DEVICE_NETWORK_FILE_SYSTEM, 123, METHOD_BUFFERED, FILE_ANY_ACCESS):
        DDbgPrint("    FILE_DEVICE_NETWORK_FILE_SYSTEM (123)\n");
        break;
        
	default:
		DDbgPrint("    Unknown FSCTL %d (%08lx)\n",
			(FsControlCode >> 2) & 0xFFF, FsControlCode);
	}
}

/*
NTSTATUS
DokanUserFsRequest(
	__in PDEVICE_OBJECT	DeviceObject,
	__in PIRP			Irp
	)
{
	NTSTATUS			status = STATUS_INVALID_DEVICE_REQUEST;
	PIO_STACK_LOCATION	irpSp = NULL;

	irpSp = IoGetCurrentIrpStackLocation(Irp);
    
	switch(irpSp->Parameters.FileSystemControl.FsControlCode) {

	case FSCTL_REQUEST_OPLOCK_LEVEL_1:
		DDbgPrint("    FSCTL_REQUEST_OPLOCK_LEVEL_1\n");
		status = STATUS_OPLOCK_NOT_GRANTED;
		break;

	case FSCTL_REQUEST_OPLOCK_LEVEL_2:
		DDbgPrint("    FSCTL_REQUEST_OPLOCK_LEVEL_2\n");
		status = STATUS_OPLOCK_NOT_GRANTED;
		break;

	case FSCTL_REQUEST_BATCH_OPLOCK:
		DDbgPrint("    FSCTL_REQUEST_BATCH_OPLOCK\n");
		status = STATUS_OPLOCK_NOT_GRANTED;
		break;

    case FSCTL_REQUEST_OPLOCK:
		DDbgPrint("    FSCTL_REQUEST_OPLOCK\n");
		status = STATUS_OPLOCK_NOT_GRANTED;
		break;
        
	case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
		DDbgPrint("    FSCTL_OPLOCK_BREAK_ACKNOWLEDGE\n");
		status = STATUS_SUCCESS;
		break;

	case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
		DDbgPrint("    FSCTL_OPBATCH_ACK_CLOSE_PENDING\n");
		status = STATUS_SUCCESS;
		break;

	case FSCTL_OPLOCK_BREAK_NOTIFY:
		DDbgPrint("    FSCTL_OPLOCK_BREAK_NOTIFY\n");
		status = STATUS_SUCCESS;
		break;

	case FSCTL_LOCK_VOLUME:
		DDbgPrint("    FSCTL_LOCK_VOLUME\n");
		status = STATUS_SUCCESS;
		break;

	case FSCTL_UNLOCK_VOLUME:
		DDbgPrint("    FSCTL_UNLOCK_VOLUME\n");
		status = STATUS_SUCCESS;
		break;

	case FSCTL_DISMOUNT_VOLUME:
		DDbgPrint("    FSCTL_DISMOUNT_VOLUME\n");
		break;

	case FSCTL_IS_VOLUME_MOUNTED:
		DDbgPrint("    FSCTL_IS_VOLUME_MOUNTED\n");
		status = STATUS_SUCCESS;
		break;

	case FSCTL_IS_PATHNAME_VALID:
		DDbgPrint("    FSCTL_IS_PATHNAME_VALID\n");
		break;

	case FSCTL_MARK_VOLUME_DIRTY:
		DDbgPrint("    FSCTL_MARK_VOLUME_DIRTY\n");
		break;

	case FSCTL_QUERY_RETRIEVAL_POINTERS:
		DDbgPrint("    FSCTL_QUERY_RETRIEVAL_POINTERS\n");
		break;

	case FSCTL_GET_COMPRESSION:
		DDbgPrint("    FSCTL_GET_COMPRESSION\n");
		break;

	case FSCTL_SET_COMPRESSION:
		DDbgPrint("    FSCTL_SET_COMPRESSION\n");
		break;

	case FSCTL_MARK_AS_SYSTEM_HIVE:
		DDbgPrint("    FSCTL_MARK_AS_SYSTEM_HIVE\n");
		break;

	case FSCTL_OPLOCK_BREAK_ACK_NO_2:
		DDbgPrint("    FSCTL_OPLOCK_BREAK_ACK_NO_2\n");
		break;

	case FSCTL_INVALIDATE_VOLUMES:
		DDbgPrint("    FSCTL_INVALIDATE_VOLUMES\n");
		break;

	case FSCTL_QUERY_FAT_BPB:
		DDbgPrint("    FSCTL_QUERY_FAT_BPB\n");
		break;

	case FSCTL_REQUEST_FILTER_OPLOCK:
		DDbgPrint("    FSCTL_REQUEST_FILTER_OPLOCK\n");
		break;

	case FSCTL_FILESYSTEM_GET_STATISTICS:
		DDbgPrint("    FSCTL_FILESYSTEM_GET_STATISTICS\n");
		break;

	case FSCTL_GET_NTFS_VOLUME_DATA:
		DDbgPrint("    FSCTL_GET_NTFS_VOLUME_DATA\n");
		break;

	case FSCTL_GET_NTFS_FILE_RECORD:
		DDbgPrint("    FSCTL_GET_NTFS_FILE_RECORD\n");
		break;

	case FSCTL_GET_VOLUME_BITMAP:
		DDbgPrint("    FSCTL_GET_VOLUME_BITMAP\n");
		break;

	case FSCTL_GET_RETRIEVAL_POINTERS:
		DDbgPrint("    FSCTL_GET_RETRIEVAL_POINTERS\n");
		break;

	case FSCTL_MOVE_FILE:
		DDbgPrint("    FSCTL_MOVE_FILE\n");
		break;

	case FSCTL_IS_VOLUME_DIRTY:
		DDbgPrint("    FSCTL_IS_VOLUME_DIRTY\n");
		break;

	case FSCTL_ALLOW_EXTENDED_DASD_IO:
		DDbgPrint("    FSCTL_ALLOW_EXTENDED_DASD_IO\n");
		break;

	case FSCTL_FIND_FILES_BY_SID:
		DDbgPrint("    FSCTL_FIND_FILES_BY_SID\n");
		break;

	case FSCTL_SET_OBJECT_ID:
		DDbgPrint("    FSCTL_SET_OBJECT_ID\n");
		break;

	case FSCTL_GET_OBJECT_ID:
		DDbgPrint("    FSCTL_GET_OBJECT_ID\n");
		break;

	case FSCTL_DELETE_OBJECT_ID:
		DDbgPrint("    FSCTL_DELETE_OBJECT_ID\n");
		break;

	case FSCTL_SET_REPARSE_POINT:
		DDbgPrint("    FSCTL_SET_REPARSE_POINT\n");
		break;

	case FSCTL_GET_REPARSE_POINT:
		DDbgPrint("    FSCTL_GET_REPARSE_POINT\n");
		status = STATUS_NOT_A_REPARSE_POINT;
		break;

	case FSCTL_DELETE_REPARSE_POINT:
		DDbgPrint("    FSCTL_DELETE_REPARSE_POINT\n");
		break;

	case FSCTL_ENUM_USN_DATA:
		DDbgPrint("    FSCTL_ENUM_USN_DATA\n");
		break;

	case FSCTL_SECURITY_ID_CHECK:
		DDbgPrint("    FSCTL_SECURITY_ID_CHECK\n");
		break;

	case FSCTL_READ_USN_JOURNAL:
		DDbgPrint("    FSCTL_READ_USN_JOURNAL\n");
		break;

	case FSCTL_SET_OBJECT_ID_EXTENDED:
		DDbgPrint("    FSCTL_SET_OBJECT_ID_EXTENDED\n");
		break;

	case FSCTL_CREATE_OR_GET_OBJECT_ID:
		DDbgPrint("    FSCTL_CREATE_OR_GET_OBJECT_ID\n");
		break;

	case FSCTL_SET_SPARSE:
		DDbgPrint("    FSCTL_SET_SPARSE\n");
		break;

	case FSCTL_SET_ZERO_DATA:
		DDbgPrint("    FSCTL_SET_ZERO_DATA\n");
		break;

	case FSCTL_QUERY_ALLOCATED_RANGES:
		DDbgPrint("    FSCTL_QUERY_ALLOCATED_RANGES\n");
		break;

	case FSCTL_SET_ENCRYPTION:
		DDbgPrint("    FSCTL_SET_ENCRYPTION\n");
		break;

	case FSCTL_ENCRYPTION_FSCTL_IO:
		DDbgPrint("    FSCTL_ENCRYPTION_FSCTL_IO\n");
		break;

	case FSCTL_WRITE_RAW_ENCRYPTED:
		DDbgPrint("    FSCTL_WRITE_RAW_ENCRYPTED\n");
		break;

	case FSCTL_READ_RAW_ENCRYPTED:
		DDbgPrint("    FSCTL_READ_RAW_ENCRYPTED\n");
		break;

	case FSCTL_CREATE_USN_JOURNAL:
        DDbgPrint("    FSCTL_CREATE_USN_JOURNAL\n");
		break;

	case FSCTL_READ_FILE_USN_DATA:
		DDbgPrint("    FSCTL_READ_FILE_USN_DATA\n");
		break;

	case FSCTL_WRITE_USN_CLOSE_RECORD:
		DDbgPrint("    FSCTL_WRITE_USN_CLOSE_RECORD\n");
		break;

	case FSCTL_EXTEND_VOLUME:
		DDbgPrint("    FSCTL_EXTEND_VOLUME\n");
		break;

	case FSCTL_QUERY_USN_JOURNAL:
		DDbgPrint("    FSCTL_QUERY_USN_JOURNAL\n");
		break;

	case FSCTL_DELETE_USN_JOURNAL:
		DDbgPrint("    FSCTL_DELETE_USN_JOURNAL\n");
		break;

	case FSCTL_MARK_HANDLE:
		DDbgPrint("    FSCTL_MARK_HANDLE\n");
		break;

	case FSCTL_SIS_COPYFILE:
		DDbgPrint("    FSCTL_SIS_COPYFILE\n");
		break;

	case FSCTL_SIS_LINK_FILES:
		DDbgPrint("    FSCTL_SIS_LINK_FILES\n");
		break;

	case FSCTL_RECALL_FILE:
		DDbgPrint("    FSCTL_RECALL_FILE\n");
		break;
        
    case CTL_CODE(FILE_DEVICE_NETWORK_FILE_SYSTEM, 123, METHOD_BUFFERED, FILE_ANY_ACCESS):
        DDbgPrint("    FILE_DEVICE_NETWORK_FILE_SYSTEM (123)\n");
        break;
        
	default:
		DDbgPrint("    Unknown FSCTL %d (%08lx)\n",
			(irpSp->Parameters.FileSystemControl.FsControlCode >> 2) & 0xFFF, irpSp->Parameters.FileSystemControl.FsControlCode);
	}

    DokanPrintNTStatus(status);
	return status;
}
*/

BOOLEAN
DokanTryHandleUserFsRequestOplock(
	__in PIRP			Irp,
    __in ULONG          FsControlCode
	)
{
    switch(FsControlCode)
    {
        case FSCTL_REQUEST_OPLOCK_LEVEL_1:
        case FSCTL_REQUEST_OPLOCK_LEVEL_2:
        case FSCTL_REQUEST_BATCH_OPLOCK:
        case FSCTL_REQUEST_OPLOCK:
            // Do not grant oplocks
            Irp->IoStatus.Status = STATUS_OPLOCK_NOT_GRANTED;
            Irp->IoStatus.Information = 0;
            
            DokanPrintNTStatus(Irp->IoStatus.Status);
            
            IoCompleteRequest(Irp, IO_NO_INCREMENT);
            return TRUE;
            
        case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
        case FSCTL_OPLOCK_BREAK_ACK_NO_2:
        case FSCTL_OPLOCK_BREAK_NOTIFY:
        case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
            Irp->IoStatus.Status = STATUS_INVALID_OPLOCK_PROTOCOL;
            Irp->IoStatus.Information = 0;
            
            DokanPrintNTStatus(Irp->IoStatus.Status);
            
            IoCompleteRequest(Irp, IO_NO_INCREMENT);
            return TRUE;
        
    }
    
    // Not handled
    return FALSE;
}

NTSTATUS
DokanUserFsRequest(
	__in PDEVICE_OBJECT	DeviceObject,
	__in PIRP			Irp,
    __out PBOOLEAN      Handled
	)
{
	NTSTATUS			status = STATUS_INVALID_DEVICE_REQUEST;
    ULONG               FsControlCode;
	PIO_STACK_LOCATION	irpSp = NULL;
    PDokanVCB			vcb;
	PDokanCCB			ccb;
	PDokanFCB			fcb;
    ULONG				eventLength;
	PEVENT_CONTEXT		eventContext;
    PVOID               eventInBuffer;
    ULONG				flags = 0;

    *Handled = FALSE;
    
	irpSp = IoGetCurrentIrpStackLocation(Irp);
    
    DDbgPrintEnterRequest("DokanUserFsRequest", irpSp);
    
    vcb = DeviceObject->DeviceExtension;
	if (GetIdentifierType(vcb) != VCB) {
		DDbgPrint("  invalid vcb\n");
        return STATUS_INVALID_PARAMETER;
	}

	ccb = irpSp->FileObject->FsContext2;
	if (ccb == NULL) {
        DDbgPrint("  invalid ccb\n");
		return STATUS_INVALID_PARAMETER;
	}
    
    FsControlCode = irpSp->Parameters.FileSystemControl.FsControlCode;
    DDbgPrintFsctl(FsControlCode);
    
    if (DokanTryHandleUserFsRequestOplock(Irp, FsControlCode))
    {
        // Handled by driver
        *Handled = TRUE;
        DDbgPrintExitRequest("DokanUserFsRequest", irpSp);
        return STATUS_SUCCESS;
    }
    
    switch(METHOD_FROM_CTL_CODE(FsControlCode))
    {
        case METHOD_NEITHER:
            DDbgPrint("  METHOD_NEITHER, MdlAddress=%08lX\n", Irp->MdlAddress);
            break;
            
        case METHOD_BUFFERED:
            DDbgPrint("  METHOD_BUFFERED, MdlAddress=%08lX\n", Irp->MdlAddress);
            break;
            
        case METHOD_IN_DIRECT:
            DDbgPrint("  METHOD_IN_DIRECT, MdlAddress=%08lX\n", Irp->MdlAddress);
            break;
            
        case METHOD_OUT_DIRECT:
            DDbgPrint("  METHOD_OUT_DIRECT, MdlAddress=%08lX\n", Irp->MdlAddress);
            break;
        
    }

    // Allocate MDL for output on a different thread    
    if (Irp->UserBuffer != NULL && irpSp->Parameters.DeviceIoControl.OutputBufferLength > 0) 
    {
        if (Irp->MdlAddress == NULL)
        {
            DDbgPrint("  allocate MDL\n");
            status = DokanAllocateMdl(Irp, irpSp->Parameters.DeviceIoControl.OutputBufferLength);
            if (!NT_SUCCESS(status)) {
                DDbgPrint("    allocate MDL failed\n");
                return status;
            }
            flags = DOKAN_MDL_ALLOCATED;
        }
    }
    
    // Compute the length of the event context that we need
    eventLength = sizeof(EVENT_CONTEXT) + irpSp->Parameters.DeviceIoControl.InputBufferLength;
    
    // Create the event context
    eventContext = AllocateEventContext(vcb->Dcb, Irp, eventLength, ccb);
    
    if (eventContext == NULL) {
        DDbgPrint("  cannot allocate EventContext\n");
		return STATUS_INSUFFICIENT_RESOURCES;
	}
    
    eventContext->Context = ccb->UserContext;
    
    // Set members on the event context
    eventContext->Fsctl.FsControlCode = FsControlCode;
    
    eventContext->Fsctl.InBufferLength = irpSp->Parameters.DeviceIoControl.InputBufferLength;
    eventContext->Fsctl.OutBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
    
    DDbgPrint("  in  buffer: %d\n", eventContext->Fsctl.InBufferLength);
    DDbgPrint("  out buffer: %d\n", eventContext->Fsctl.OutBufferLength);
    
    // Set convenience pointer
    eventInBuffer = &(eventContext->Fsctl.InBuffer);
    
    // Copy data into the event context
    if (irpSp->Parameters.DeviceIoControl.InputBufferLength > 0)
    {
        if (METHOD_FROM_CTL_CODE(FsControlCode) == METHOD_NEITHER)
        {
            DDbgPrint("    METHOD_NIETHER\n");
            RtlCopyMemory(eventInBuffer, irpSp->Parameters.DeviceIoControl.Type3InputBuffer, irpSp->Parameters.DeviceIoControl.InputBufferLength);
        }
        else
        {
            DDbgPrint("    not METHOD_NIETHER\n");
            RtlCopyMemory(eventInBuffer, Irp->AssociatedIrp.SystemBuffer, irpSp->Parameters.DeviceIoControl.InputBufferLength);
        }
    }
    
    status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, flags);
    DokanPrintNTStatus(status);
    DDbgPrintExitRequest("DokanUserFsRequest", irpSp);
    
    return status;
}

VOID
DokanCompleteUserFsRequest(
	__in PIRP_ENTRY			IrpEntry,
	__in PEVENT_INFORMATION	EventInfo
	)
{
	PIRP				irp;
	PIO_STACK_LOCATION	irpSp = NULL;
	NTSTATUS			status   = STATUS_INVALID_DEVICE_REQUEST;
	ULONG				info	 = 0;
	ULONG				bufferLen= 0;
	PVOID				buffer	 = NULL;

	irp   = IrpEntry->Irp;
	irpSp = IrpEntry->IrpSp;	
    
    DDbgPrintEnterComplete("DokanUserFsRequest", irpSp);

	if (irp->MdlAddress) {
		buffer = MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
	}
    
	// usable buffer size
	bufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength;

    if (bufferLen == 0 || buffer == NULL || bufferLen < EventInfo->BufferLength) {
        // buffer is not specified or short of length
		info   = 0;
		status = EventInfo->Status;;

	} else {

		PDokanCCB ccb	= IrpEntry->FileObject->FsContext2;

		//
		// set the information recieved from user mode
		//
		ASSERT(buffer != NULL);
		
        RtlZeroMemory(buffer, bufferLen);
		
		RtlCopyMemory(buffer, EventInfo->Buffer, EventInfo->BufferLength);

		ccb->UserContext = EventInfo->Context;

        // Dump buffer
        DDbgPrint("  count: %d\n", EventInfo->BufferLength);
        DDbgMemory(EventInfo->Buffer, EventInfo->BufferLength);
        
		status = EventInfo->Status;
		info = EventInfo->BufferLength;

	}


	if (IrpEntry->Flags & DOKAN_MDL_ALLOCATED) {
		DokanFreeMdl(irp);
		IrpEntry->Flags &= ~DOKAN_MDL_ALLOCATED;
	}

	irp->IoStatus.Status = status;
	irp->IoStatus.Information = info;

    DokanPrintNTStatus(status);
    DDbgPrintExitComplete("DokanUserFsRequest", irpSp);

	IoCompleteRequest(irp, IO_NO_INCREMENT);

}


NTSTATUS
DokanDispatchFileSystemControl(
	__in PDEVICE_OBJECT DeviceObject,
	__in PIRP Irp
	)
{
	NTSTATUS			status = STATUS_INVALID_PARAMETER;
	PIO_STACK_LOCATION	irpSp = NULL;
	PDokanVCB			vcb;
    BOOLEAN             Handled = FALSE;

	PAGED_CODE();

	__try {
		FsRtlEnterFileSystem();

		DDbgPrint("==> DokanFileSystemControl [Request]\n");
		DDbgPrint("  ProcessId %lu\n", IoGetRequestorProcessId(Irp));

		vcb = DeviceObject->DeviceExtension;
		if (GetIdentifierType(vcb) != VCB) {
            DDbgPrint("  invalid vcb\n");
			status = STATUS_INVALID_PARAMETER;
			__leave;
		}

		irpSp = IoGetCurrentIrpStackLocation(Irp);

        DDbgPrintEnterRequest("DokanFileSystemControl", irpSp);
        
		switch(irpSp->MinorFunction) {
		case IRP_MN_KERNEL_CALL:
			DDbgPrint("	 IRP_MN_KERNEL_CALL\n");
			break;

		case IRP_MN_LOAD_FILE_SYSTEM:
			DDbgPrint("	 IRP_MN_LOAD_FILE_SYSTEM\n");
			break;

		case IRP_MN_MOUNT_VOLUME:
			{
				PVPB vpb;
				DDbgPrint("	 IRP_MN_MOUNT_VOLUME\n");
				if (irpSp->Parameters.MountVolume.DeviceObject != vcb->Dcb->DeviceObject) {
					DDbgPrint("   Not DokanDiskDevice\n");
					status = STATUS_INVALID_PARAMETER;
				}
				vpb = irpSp->Parameters.MountVolume.Vpb;
				vpb->DeviceObject = vcb->DeviceObject;
				vpb->RealDevice = vcb->DeviceObject;
				vpb->Flags |= VPB_MOUNTED;
				vpb->VolumeLabelLength = wcslen(VOLUME_LABEL) * sizeof(WCHAR);
				RtlStringCchCopyW(vpb->VolumeLabel, sizeof(vpb->VolumeLabel) / sizeof(WCHAR), VOLUME_LABEL);
				vpb->SerialNumber = 0x19831116;
				status = STATUS_SUCCESS;
			}
			break;

		case IRP_MN_USER_FS_REQUEST:
			DDbgPrint("	 IRP_MN_USER_FS_REQUEST\n");
			status = DokanUserFsRequest(DeviceObject, Irp, &Handled);
			break;

		case IRP_MN_VERIFY_VOLUME:
			DDbgPrint("	 IRP_MN_VERIFY_VOLUME\n");
			break;

		default:
			DDbgPrint("  unknown %d\n", irpSp->MinorFunction);
			status = STATUS_INVALID_PARAMETER;
			break;

		}

	} __finally {
		
        if (!Handled)
        {
            if (status != STATUS_PENDING)
            {
                // Irp needs to be handled with the given status
                Irp->IoStatus.Status = status;
                Irp->IoStatus.Information = 0;
                
                DokanPrintNTStatus(status);
                DDbgPrintExitRequest("DokanFileSystemControl", irpSp);
                
                IoCompleteRequest(Irp, IO_NO_INCREMENT);
            }
            else
            {
                // Irp is pending
                DokanPrintNTStatus(status);
                DDbgPrintExitRequest("DokanFileSystemControl", irpSp);
            }
        }
        else
        {
            // Irp was already handled
            DDbgPrintExitRequest("DokanFileSystemControl", irpSp);
        }
        
		FsRtlExitFileSystem();
	}

	return status;
}

